﻿#include "HnrjitMarquee.h"
#include "HnrjitContainer.h"

CHnrjitMarquee::CHnrjitMarquee()
	: CHnrjitControl()
	, m_iAppearMode(kDefault)
	, m_hThread(NULL)
	, m_nInterval(100)
	, m_nStep(10)
	, m_lPosition(0)
	, m_lWidth(0)
{
	m_hQuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	m_hResetEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	InitializeCriticalSection(&m_csSynch);
}

CHnrjitMarquee::~CHnrjitMarquee()
{
	if (m_hThread)
	{
		SetEvent(m_hQuitEvent);
		WaitForSingleObject(m_hThread, INFINITE);
	}
	CloseHandle(m_hQuitEvent);
	CloseHandle(m_hResetEvent);
	DeleteCriticalSection(&m_csSynch);
}

void CHnrjitMarquee::SetAttribute(const TiXmlElement* pxElement, HZIP hZipRes /*= NULL*/)
{
	CHnrjitControl::SetAttribute(pxElement, hZipRes);

	const CHAR* pszValue;
	if (pszValue = pxElement->Attribute("Mode"))
	{
		if (strcmp(pszValue, "default") == 0)
		{
			SetMode(CHnrjitMarquee::kDefault);
		}
		else if (strcmp(pszValue, "always") == 0)
		{
			SetMode(CHnrjitMarquee::kAlways);
		}
	}
	if (pszValue = pxElement->Attribute("Interval"))
	{
		DWORD dwInterval = strtol(pszValue, NULL, 10);
		SetInterval(dwInterval);
	}
	if (pszValue = pxElement->Attribute("Step"))
	{
		DWORD dwStep = strtol(pszValue, NULL, 10);
		SetStep(dwStep);
	}
}

void CHnrjitMarquee::SetTitle(const TCHAR* pszTitle)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	EnterCriticalSection(&m_csSynch);
	if (m_strTitle != pszTitle)
	{
		m_strTitle = pszTitle;

		if (m_iAppearMode == kDefault)
		{
			RECT calcRect = { 0 };
			HDC hdcMemory = CreateCompatibleDC(NULL);
			if (hdcMemory)
			{
				LOGFONT LogFont;
				GetFont(&LogFont);
				HFONT hNewFont = CreateFontIndirect(&LogFont);
				if (hNewFont)
				{
					HGDIOBJ hOldFont = SelectObject(hdcMemory, hNewFont);
					DrawText(hdcMemory, m_strTitle, -1, &calcRect, DT_CALCRECT);
					SelectObject(hdcMemory, hOldFont);

					DeleteObject(hNewFont);
				}
				DeleteDC(hdcMemory);
			}

			m_lWidth = calcRect.right - calcRect.left;
		}

		ActivateMarquee();

		Invalidate(m_rcGeometry);
	}
	LeaveCriticalSection(&m_csSynch);
}

void CHnrjitMarquee::SetTextAlign(UINT nTextAlign)
{
	nTextAlign |= DT_EDITCONTROL;
	CHnrjitControl::SetTextAlign(nTextAlign);
}

CHnrjitMarquee::AppearMode CHnrjitMarquee::GetMode() const
{
	return m_iAppearMode;
}

void CHnrjitMarquee::SetMode(AppearMode iAppearMode)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	EnterCriticalSection(&m_csSynch);
	m_iAppearMode = iAppearMode;

	ActivateMarquee();
	LeaveCriticalSection(&m_csSynch);
}

UINT CHnrjitMarquee::GetInterval() const
{
	return m_nInterval;
}

void CHnrjitMarquee::SetInterval(UINT nInterval)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	EnterCriticalSection(&m_csSynch);
	m_nInterval = nInterval;

	ActivateMarquee();
	LeaveCriticalSection(&m_csSynch);
}

UINT CHnrjitMarquee::GetStep() const
{
	return m_nStep;
}

void CHnrjitMarquee::SetStep(UINT nStep)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	EnterCriticalSection(&m_csSynch);
	m_nStep = nStep;

	ActivateMarquee();
	LeaveCriticalSection(&m_csSynch);
}

DWORD CHnrjitMarquee::MarqueeThreadProc()
{
	DWORD dwResult = 0;
	HANDLE hObjects[] = { m_hQuitEvent, m_hResetEvent };
	for (;;)
	{
		dwResult = WaitForMultipleObjects(_countof(hObjects), hObjects, FALSE, m_nInterval);

		if (dwResult == WAIT_OBJECT_0) break;

		if (dwResult == WAIT_TIMEOUT)
		{
			EnterCriticalSection(&m_csSynch);
			if (m_nTextAlign & DT_RTLREADING)
			{
				// 跑马灯向右滚动
				if (m_lPosition > m_lWidth)
				{
					// 跑马灯滚出了右边框
					m_lPosition = 0 - m_lWidth;
				}
				else
				{
					m_lPosition += m_nStep;
				}
			}
			else
			{
				// 跑马灯向左滚动
				if (0 - m_lPosition > m_lWidth)
				{
					// 跑马灯滚出了左边框
					m_lPosition = m_lWidth;
				}
				else
				{
					m_lPosition -= m_nStep;
				}
			}
			Invalidate(GetGeometry());
			LeaveCriticalSection(&m_csSynch);
		}
	}

	EnterCriticalSection(&m_csSynch);
	CloseHandle(m_hThread);
	m_hThread = NULL;
	LeaveCriticalSection(&m_csSynch);

	return 0;
}

DWORD CHnrjitMarquee::MarqueeThreadProc(void* lpParam)
{
	return ((CHnrjitMarquee*) lpParam)->MarqueeThreadProc();
}

void CHnrjitMarquee::ActivateMarquee()
{
	if (m_iAppearMode == kAlways)
	{
		m_lWidth = m_rcGeometry.Width();
	}
	if (m_lWidth >= m_rcGeometry.Width())
	{
		// 文本内容过长，自动开启跑马灯
		if (m_hThread)
		{
			SetEvent(m_hResetEvent);
		}
		else
		{
			ResetEvent(m_hQuitEvent);
			m_hThread = CreateThread(NULL, 0, MarqueeThreadProc, this, 0, NULL);
		}
	}
	else
	{
		if (m_hThread)
		{
			ResetEvent(m_hQuitEvent);
		}
	}
}

void CHnrjitMarquee::DrawControl(HDC hdc)
{
	CRect rcReference = CRect(GetLocation() + m_pContainer->GetLocation(), GetSize());

	ControlStatus iControlStatus = GetControlState();
	if (m_spBackIcons[iControlStatus])
	{
		DrawImage(hdc, rcReference, iControlStatus);
	}
	else
	{
		if (iControlStatus == kActivated && m_spBackIcons[kNormal])
		{
			// 处于激活状态但无激活时图片
			DrawImage(hdc, rcReference, kNormal);
		}
		else
		{
			DrawRect(hdc, rcReference, iControlStatus);
		}
	}

	HDC hdcMemory = CreateCompatibleDC(hdc);
	if (hdcMemory)
	{
		HBITMAP hNewBitmap = CreateCompatibleBitmap(hdc, rcReference.Width(), rcReference.Height());
		if (hNewBitmap)
		{
			HGDIOBJ hOldBitmap = SelectObject(hdcMemory, hNewBitmap);

			SetBkMode(hdcMemory, TRANSPARENT);

			rcReference.MoveToXY(0, 0);
			if (m_lWidth >= rcReference.Width())
			{
				rcReference.MoveToX(m_lPosition);
			}
			DrawTitle(hdcMemory, rcReference, iControlStatus);

			TransparentBlt(hdc, m_rcGeometry.left, m_rcGeometry.top, m_rcGeometry.Width(), m_rcGeometry.Height(), hdcMemory,
				0, 0, rcReference.Width(), rcReference.Height(), RGB(0x00, 0x00, 0x00));

			SelectObject(hdcMemory, hOldBitmap);
			DeleteObject(hNewBitmap);
		}
		DeleteDC(hdcMemory);
	}
}
